package org.ravenhearte.hardboild.gui;

import java.nio.FloatBuffer;

import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.image.Texture2D;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.renderer.TextureRenderer;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.TexCoords;
import com.jme.scene.state.BlendState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.util.geom.BufferUtils;

public class ImposterElement extends ImageElement {
	public ImposterElement(String name, int size) {
		super(name, size, size);
		
		DisplaySystem display = DisplaySystem.getDisplaySystem();
		
		// to handle texture transparency:
		// create a blend state
		final BlendState bs = display.getRenderer().createBlendState();
		// activate blending
		bs.setBlendEnabled(true);
		// set the source function
		bs.setSourceFunctionAlpha(BlendState.SourceFunction.SourceAlpha);
		// set the destination function
		bs.setDestinationFunctionAlpha(BlendState.DestinationFunction.OneMinusSourceAlpha);
		// set the blend equation between source and destination
		bs.setBlendEquation(BlendState.BlendEquation.Subtract);
		bs.setTestEnabled(true);
		bs.setTestFunction(BlendState.TestFunction.GreaterThan);
		// activate the blend state
		bs.setEnabled(true);
		// assign the blend state to the quad
		spatial.setRenderState(bs);
	}
	
	public void setTexture(Spatial imposter) {
		Node fakeScene = new Node("Fake node");
		fakeScene.setRenderQueueMode(Renderer.QUEUE_SKIP);
		fakeScene.attachChild(imposter);

		ZBufferState buf = DisplaySystem.getDisplaySystem()
			.getRenderer().createZBufferState();
		buf.setEnabled(true);
		buf.setFunction(ZBufferState.TestFunction.LessThanOrEqualTo);
		fakeScene.setRenderState(buf);

		TextureRenderer tRenderer = DisplaySystem.getDisplaySystem()
			.createTextureRenderer((int)spatial.getWidth(),
			(int)spatial.getWidth(), TextureRenderer.Target.Texture2D);
		tRenderer.setBackgroundColor(new ColorRGBA(0, 0, 0, 0));
		Texture2D fakeTex = new Texture2D();
		fakeTex.setWrap(Texture.WrapMode.Clamp);
		
		imposter.updateGeometricState(0f, true);
		
		BoundingBox box = (BoundingBox)imposter.getWorldBound();
				
		float size = box.zExtent;

		tRenderer.setupTexture(fakeTex);
		tRenderer.getCamera().setLocation((new Vector3f(0, 0, 0)).add(0f, size * 2.5f, size));
		tRenderer.getCamera().lookAt(new Vector3f(0, 0, size), new Vector3f(0, 0, 1f));
		tRenderer.getCamera().setLeft(new Vector3f(-1, 0, 0));
		
		Vector3f saveLoc = imposter.getLocalTranslation();
		imposter.setLocalTranslation(0, 0, 0);
		fakeScene.updateGeometricState(0.0f, true);
		fakeScene.updateRenderState();
		tRenderer.render(fakeScene, fakeTex);
		imposter.setLocalTranslation(saveLoc);

		// Now add that texture to the "real" cube.
		TextureState ts = DisplaySystem.getDisplaySystem()
			.getRenderer().createTextureState();
		ts.setEnabled(true);
		ts.setTexture(fakeTex, 0);
		
        int textureWidth = tRenderer.getWidth();
        int textureHeight = tRenderer.getHeight();
        
        final FloatBuffer texCoords = BufferUtils.createVector2Buffer(4);
        
        texCoords.put((float)x / textureWidth).put(
        		1 - (float) y / textureHeight);
        
        texCoords.put((float)x / textureWidth).put(
        		1 - (float) (y + spatial.getHeight()) / textureHeight);
        
        texCoords.put((float)(x +  spatial.getWidth()) / textureWidth).put(
        		1 - (float) (y + spatial.getHeight()) / textureHeight);
        
        texCoords.put((float)(x +  spatial.getWidth()) / textureWidth).put(
        		1 - (float) y / textureHeight);

        // assign texture coordinates to the quad
        spatial.setTextureCoords(new TexCoords(texCoords));
		
		spatial.setRenderState(ts);
	}
}